 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.update.internal.ui.wizards;

 import java.lang.reflect.InvocationTargetException ;
 import java.net.URL ;
 import java.util.ArrayList ;
 import java.util.Arrays ;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.SubProgressMonitor;
 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
 import org.eclipse.core.runtime.jobs.IJobChangeListener;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
 import org.eclipse.jface.dialogs.ErrorDialog;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.wizard.IWizardPage;
 import org.eclipse.jface.wizard.Wizard;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.update.configuration.IInstallConfiguration;
 import org.eclipse.update.core.IFeature;
 import org.eclipse.update.core.IFeatureReference;
 import org.eclipse.update.core.IIncludedFeatureReference;
 import org.eclipse.update.core.ISite;
 import org.eclipse.update.core.ISiteFeatureReference;
 import org.eclipse.update.core.IVerificationListener;
 import org.eclipse.update.core.SiteManager;
 import org.eclipse.update.core.model.InstallAbortedException;
 import org.eclipse.update.internal.core.FeatureDownloadException;
 import org.eclipse.update.internal.core.LiteFeature;
 import org.eclipse.update.internal.core.UpdateCore;
 import org.eclipse.update.internal.operations.DuplicateConflictsValidator;
 import org.eclipse.update.internal.operations.InstallOperation;
 import org.eclipse.update.internal.operations.UpdateUtils;
 import org.eclipse.update.internal.ui.UpdateUI;
 import org.eclipse.update.internal.ui.UpdateUIImages;
 import org.eclipse.update.internal.ui.UpdateUIMessages;
 import org.eclipse.update.internal.ui.security.JarVerificationService;
 import org.eclipse.update.operations.IBatchOperation;
 import org.eclipse.update.operations.IFeatureOperation;
 import org.eclipse.update.operations.IInstallFeatureOperation;
 import org.eclipse.update.operations.IOperation;
 import org.eclipse.update.operations.IOperationListener;
 import org.eclipse.update.operations.OperationsManager;
 import org.eclipse.update.search.UpdateSearchRequest;


 public class InstallWizard2
     extends Wizard
     implements IOperationListener, ISearchProvider {
     private ReviewPage reviewPage;
     private LicensePage licensePage;
     private OptionalFeaturesPage optionalFeaturesPage;
     private TargetPage targetPage;
     private IInstallConfiguration config;
     private int installCount = 0;
     private UpdateSearchRequest searchRequest;
     private ArrayList jobs;
     private boolean needsRestart;
     private static boolean isRunning;
     private IBatchOperation installOperation;
     private Job job;
     public static final Object jobFamily = new Object ();
     private IJobChangeListener jobListener;
     private boolean isUpdate;

     public InstallWizard2(UpdateSearchRequest searchRequest, IInstallFeatureOperation[] jobs, boolean isUpdate) {
         this (searchRequest, new ArrayList (Arrays.asList(jobs)), isUpdate);
     }

     public InstallWizard2(UpdateSearchRequest searchRequest, ArrayList jobs, boolean isUpdate) {
         this.isUpdate = isUpdate;
         this.searchRequest = searchRequest;
         this.jobs = jobs;
         isRunning = true;
         setDialogSettings(UpdateUI.getDefault().getDialogSettings());
         setDefaultPageImageDescriptor(UpdateUIImages.DESC_UPDATE_WIZ);
         setForcePreviousAndNextButtons(true);
         setNeedsProgressMonitor(true);
         setWindowTitle(UpdateUIMessages.InstallWizard_wtitle);
     }
     
     public int getInstallCount() {
         return installCount;
     }
     public boolean isRestartNeeded() {
         return installCount > 0 && needsRestart; // or == selectedJobs.length
 }

     public boolean performCancel() {
         isRunning = false;
         if (targetPage != null)
             targetPage.removeAddedSites();
         return super.performCancel();
     }
     /**
      * @see Wizard#performFinish()
      */
     public boolean performFinish() {
         IInstallFeatureOperation[] selectedJobs = reviewPage.getSelectedJobs();
         
         // Check for duplication conflicts
 ArrayList conflicts = DuplicateConflictsValidator.computeDuplicateConflicts(selectedJobs, config);
         if (conflicts != null) {
             DuplicateConflictsDialog dialog = new DuplicateConflictsDialog(getShell(), conflicts);
             if (dialog.open() != 0)
                 return false;
         }
         
         if (Job.getJobManager().find(jobFamily).length > 0) {
             // another update/install job is running, need to wait to finish or cancel old job
 boolean proceed = MessageDialog.openQuestion(
                     UpdateUI.getActiveWorkbenchShell(),
                     UpdateUIMessages.InstallWizard_anotherJobTitle,
                     UpdateUIMessages.InstallWizard_anotherJob);
             if (!proceed)
                 return false; // cancel this job, and let the old one go on
 }
         
         // set the install operation
 installOperation = getBatchInstallOperation(selectedJobs);
         if (installOperation != null)
             launchInBackground();
         return true;
     }

     public void addPages() {
         reviewPage = new ReviewPage(isUpdate, searchRequest, jobs);
         addPage(reviewPage);

         try {
 // config = UpdateUtils.createInstallConfiguration();
 config = SiteManager.getLocalSite().getCurrentConfiguration();
         } catch (CoreException e) {
             UpdateUI.logException(e);
         }

         licensePage = new LicensePage(true);
         addPage(licensePage);
         optionalFeaturesPage = new OptionalFeaturesPage(config);
         addPage(optionalFeaturesPage);
         targetPage = new TargetPage(config, isUpdate);
         addPage(targetPage);
     }


     private boolean isPageRequired(IWizardPage page) {
         if (page == null)
             return false;
             
         if (page.equals(licensePage)) {
             return OperationsManager.hasSelectedJobsWithLicenses(
                 reviewPage.getSelectedJobs());
         }
         if (page.equals(optionalFeaturesPage)) {
             return OperationsManager.hasSelectedJobsWithOptionalFeatures(
                 reviewPage.getSelectedJobs());
         }
         if (page.equals(targetPage)) {
             return reviewPage.getSelectedJobs().length > 0;
         }
         return true;
     }

     public IWizardPage getNextPage(IWizardPage page) {
         IWizardPage[] pages = getPages();
         boolean start = false;
         IWizardPage nextPage = null;

         if (page.equals(reviewPage)) {
             updateDynamicPages();
         }

         for (int i = 0; i < pages.length; i++) {
             if (pages[i].equals(page)) {
                 start = true;
             } else if (start) {
                 if (isPageRequired(pages[i])) {
                     nextPage = pages[i];
                     break;
                 }
             }
         }
         return nextPage;
     }

     private void updateDynamicPages() {
         if (licensePage != null) {
             IInstallFeatureOperation[] licenseJobs =
                 OperationsManager.getSelectedJobsWithLicenses(
                     reviewPage.getSelectedJobs());
             licensePage.setJobs(licenseJobs);
         }
         if (optionalFeaturesPage != null) {
             IInstallFeatureOperation[] optionalJobs =
                 OperationsManager.getSelectedJobsWithOptionalFeatures(
                     reviewPage.getSelectedJobs());
             optionalFeaturesPage.setJobs(optionalJobs);
         }
         if (targetPage != null) {
             IInstallFeatureOperation[] installJobs =
                 reviewPage.getSelectedJobs();
             targetPage.setJobs(installJobs);
         }
     }

     public boolean canFinish() {
         IWizardPage page = getContainer().getCurrentPage();
         return page.equals(targetPage) && page.isPageComplete();
     }

     private void preserveOriginatingURLs(
         IFeature feature,
         IFeatureReference[] optionalFeatures) {
         // walk the hierarchy and preserve the originating URL
 // for all the optional features that are not chosen to
 // be installed.
 URL url = feature.getSite().getURL();
         try {
             IIncludedFeatureReference[] irefs =
                 feature.getIncludedFeatureReferences();
             for (int i = 0; i < irefs.length; i++) {
                 IIncludedFeatureReference iref = irefs[i];
                 boolean preserve = false;
                 if (iref.isOptional()) {
                     boolean onTheList = false;
                     for (int j = 0; j < optionalFeatures.length; j++) {
                         if (optionalFeatures[j].equals(iref)) {
                             //was on the list
 onTheList = true;
                             break;
                         }
                     }
                     if (!onTheList)
                         preserve = true;
                 }
                 if (preserve) {
                     try {
                         String id =
                             iref.getVersionedIdentifier().getIdentifier();
                         UpdateUI.setOriginatingURL(id, url);
                     } catch (CoreException e) {
                         // Silently ignore
 }
                 } else {
                     try {
                         IFeature ifeature = iref.getFeature(null);
                         preserveOriginatingURLs(ifeature, optionalFeatures);
                     } catch (CoreException e) {
                         // Silently ignore
 }
                 }
             }
         } catch (CoreException e) {
             // Silently ignore
 }
     }

     /* (non-Javadoc)
      * @see org.eclipse.update.operations.IOperationListener#afterExecute(org.eclipse.update.operations.IOperation)
      */
     public boolean afterExecute(IOperation operation, Object data) {
         if (!(operation instanceof IInstallFeatureOperation))
             return true;
         IInstallFeatureOperation job = (IInstallFeatureOperation) operation;
         IFeature oldFeature = job.getOldFeature();
         if (oldFeature == null && job.getOptionalFeatures() != null)
             preserveOriginatingURLs(
                 job.getFeature(),
                 job.getOptionalFeatures());

         installCount++;
         return true;
     }

     /* (non-Javadoc)
      * @see org.eclipse.update.operations.IOperationListener#beforeExecute(org.eclipse.update.operations.IOperation)
      */
     public boolean beforeExecute(IOperation operation, Object data) {
         return true;
     }
     /* (non-Javadoc)
      * @see org.eclipse.update.internal.ui.wizards.ISearchProvider2#getSearchRequest()
      */
     public UpdateSearchRequest getSearchRequest() {
         return searchRequest;
     }

     public static synchronized boolean isRunning() {
         return isRunning || Job.getJobManager().find(jobFamily).length > 0;
     }
     
     private IBatchOperation getBatchInstallOperation(final IInstallFeatureOperation[] selectedJobs) {
         final IVerificationListener verificationListener =new JarVerificationService(
                 //InstallWizard.this.getShell());
 UpdateUI.getActiveWorkbenchShell());
         
         // setup jobs with the correct environment
 IInstallFeatureOperation[] operations = new IInstallFeatureOperation[selectedJobs.length];
         for (int i = 0; i < selectedJobs.length; i++) {
             IInstallFeatureOperation job = selectedJobs[i];
             IFeature[] unconfiguredOptionalFeatures = null;
             IFeatureReference[] optionalFeatures = null;
             if (UpdateUtils.hasOptionalFeatures(job.getFeature())) {
                 optionalFeatures = optionalFeaturesPage.getCheckedOptionalFeatures(job);
                 unconfiguredOptionalFeatures = optionalFeaturesPage.getUnconfiguredOptionalFeatures(job, job.getTargetSite());
             }
             IInstallFeatureOperation op =
                 OperationsManager
                     .getOperationFactory()
                     .createInstallOperation(
                     job.getTargetSite(),
                     job.getFeature(),
                     optionalFeatures,
                     unconfiguredOptionalFeatures,
                     verificationListener);
             operations[i] = op;
         }
         return OperationsManager.getOperationFactory().createBatchInstallOperation(operations);
     }



     private void launchInBackground() {
         // Downloads the feature content in the background.
 // The job listener will then install the feature when download is finished.

         // TODO: should we cancel existing jobs?
 if (jobListener != null)
             Job.getJobManager().removeJobChangeListener(jobListener);
         if (job != null)
             Job.getJobManager().cancel(job);
         jobListener = new UpdateJobChangeListener();
         Job.getJobManager().addJobChangeListener(jobListener);
         
         job = new Job(UpdateUIMessages.InstallWizard_jobName) {
             public IStatus run(IProgressMonitor monitor) {
                 if (download(monitor))
                     return Status.OK_STATUS;
                 else {
                     isRunning = false;
                     return Status.CANCEL_STATUS;
                 }
             }
             public boolean belongsTo(Object family) {
                 return InstallWizard2.jobFamily == family;
             }
         };

         job.setUser(true);
         job.setPriority(Job.INTERACTIVE);
 // if (wait) {
 // progressService.showInDialog(workbench.getActiveWorkbenchWindow().getShell(), job);
 // }
 job.schedule();
     }
     
     private boolean install(IProgressMonitor monitor) {
         // Installs the (already downloaded) features and prompts for restart
 try {
             needsRestart = installOperation.execute(monitor, InstallWizard2.this);
             UpdateUI.getStandardDisplay().asyncExec(new Runnable () {
                 public void run() {
                     UpdateUI.requestRestart(InstallWizard2.this.isRestartNeeded());
                 }
             });
         } catch (final InvocationTargetException e) {
             final Throwable targetException = e.getTargetException();
             if (!(targetException instanceof InstallAbortedException)){
                 UpdateUI.getStandardDisplay().syncExec(new Runnable () {
                     public void run() {
                         UpdateUI.logException(targetException);
                     }
                 });
             }
             return false;
         } catch (CoreException e) {
             return false;
         } finally {
             isRunning = false;
         }
         return true;
     }
     
     private boolean download(final IProgressMonitor monitor) {
         // Downloads the feature content.
 // This method is called from a background job.
 // If download fails, the user is prompted to retry.
 try {
             IFeatureOperation[] ops = installOperation.getOperations();
             monitor.beginTask(UpdateUIMessages.InstallWizard_download, 5 * ops.length);
             for (int i = 0; i < ops.length; i++) {
                 IInstallFeatureOperation op = (IInstallFeatureOperation)ops[i];
                 
                 try {
                     String featureName = op.getFeature().getLabel();
                     if ((featureName == null ) || (featureName.trim() == "") ) { //$NON-NLS-1$
 featureName = op.getFeature().getVersionedIdentifier().getIdentifier();
                     }
                     SubProgressMonitor featureDownloadMonitor = new SubProgressMonitor(monitor, 2);
                     
                     featureDownloadMonitor.beginTask(featureName, 2);
                     featureDownloadMonitor.subTask(NLS.bind(UpdateUIMessages.InstallWizard_downloadingFeatureJar, featureName));
                     
                     if (op.getFeature() instanceof LiteFeature) {
                         ISiteFeatureReference featureReference = getFeatureReference(op.getFeature());
                         IFeature feature = featureReference.getFeature(featureDownloadMonitor);
                         if (op instanceof InstallOperation) {
                             ((InstallOperation)op).setFeature(feature);
                         }
                     }
                     //featureDownloadMonitor.worked(1);
 SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, 3);
                     UpdateUtils.downloadFeatureContent(op.getTargetSite(), op.getFeature(), op.getOptionalFeatures(), subMonitor);
                 } catch (final CoreException e) {
                     if(e instanceof FeatureDownloadException){
                         boolean retry = retryDownload((FeatureDownloadException)e);
                         if (retry) {
                             // redownload current feature
 i--;
                             continue;
                         }
                     } else {
                         UpdateCore.log(e);
                         if ( !monitor.isCanceled()) {
                             Display.getDefault().syncExec( new Runnable () {
                                 public void run() {
                                     IStatus status = new Status( IStatus.ERROR, UpdateUI.getPluginId(), IStatus.OK, UpdateUIMessages.InstallWizard2_updateOperationHasFailed, e);
                                     ErrorDialog.openError(null, null, null, status);
                                 
                                 }
                             });
                         }
                     }
                     return false;
                 }
             }
             return true;
         } finally {
             monitor.done();
         }
     }
     
     private boolean retryDownload(final FeatureDownloadException e) {

         final boolean retry[] = new boolean[1];
         UpdateUI.getStandardDisplay().syncExec(new Runnable () {
             public void run() {
                 retry[0] = MessageDialog.openQuestion(
                     UpdateUI.getActiveWorkbenchShell(),
                     UpdateUIMessages.InstallWizard_retryTitle,
                     e.getMessage()+"\n" //$NON-NLS-1$
 + UpdateUIMessages.InstallWizard_retry);
             }
         });
         return retry[0];
     }
     

     private class UpdateJobChangeListener extends JobChangeAdapter {
         public void done(final IJobChangeEvent event) {
             // the job listener is triggered when the download job is done, and it proceeds
 // with the actual install
 if (event.getJob() == InstallWizard2.this.job && event.getResult() == Status.OK_STATUS) {
                 Job.getJobManager().removeJobChangeListener(this);
                 Job.getJobManager().cancel(job);
                 
                 Job installJob = new Job(UpdateUIMessages.InstallWizard_jobName) {
                     public IStatus run(IProgressMonitor monitor) {
                         //install(monitor);
 if (install(monitor)) {
                             return Status.OK_STATUS;
                         } else {
                             isRunning = false;
                             return Status.CANCEL_STATUS;
                         }
                     }
                     public boolean belongsTo(Object family) {
                         return InstallWizard2.jobFamily == family;
                     }
                 };

                 installJob.setUser(true);
                 installJob.setPriority(Job.INTERACTIVE);
 // if (wait) {
 // progressService.showInDialog(workbench.getActiveWorkbenchWindow().getShell(), job);
 // }
 installJob.schedule();
                 
                 /*final IProgressService progressService= PlatformUI.getWorkbench().getProgressService();
                 UpdateUI.getStandardDisplay().asyncExec(new Runnable() {
                     public void run() {
                         try {
                             progressService.busyCursorWhile( new IRunnableWithProgress() {
                                 public void run(final IProgressMonitor monitor){
                                     install(monitor);
                                 }
                             });
                         } catch (InvocationTargetException e) {
                             UpdateUI.logException(e);
                         } catch (InterruptedException e) {
                             UpdateUI.logException(e, false);
                         }
                     }
                 }); */
             } else if (event.getJob() == InstallWizard2.this.job && event.getResult() != Status.OK_STATUS) {
                 isRunning = false;
                 Job.getJobManager().removeJobChangeListener(this);
                 Job.getJobManager().cancel(job);
                 UpdateUI.getStandardDisplay().syncExec(new Runnable () {
                     public void run() {
                         UpdateUI.log(event.getResult(), true);
                     }
                 });
             }
         }
     }
     
     
     public ISiteFeatureReference getFeatureReference(IFeature feature) {

         ISite site = feature.getSite();
         ISiteFeatureReference[] references = site.getFeatureReferences();
         ISiteFeatureReference currentReference = null;
         for (int i = 0; i < references.length; i++) {
             currentReference = references[i];
             try {
                 if (feature.getVersionedIdentifier().equals(currentReference.getVersionedIdentifier()))
                     return currentReference;
             } catch (CoreException e) {
                 // TODO Auto-generated catch block
 e.printStackTrace();
             }
         }

         UpdateCore.warn("Feature " + feature + " not found on site" + site.getURL()); //$NON-NLS-1$ //$NON-NLS-2$
 return null;
     }

 }

